package org.go.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.go.log.Log;
import org.go.log.LoggerFactory;

/**
 * <p>
 * A <code>ConnectionProvider</code> that provides connections from a <code>DataSource</code>
 * that is managed by an application server, and made available via JNDI.
 * </p>
 * 
 * @see DBConnectionManager
 * @see ConnectionProvider
 * @see PoolingConnectionProvider
 * 
 * @author James House
 * @author Sharada Jambula
 * @author Mohammad Rezaei
 * @author Patrick Lightbody
 * @author Srinivas Venkatarangaiah
 */
public class JNDIConnectionProvider implements ConnectionProvider {

	private static final Log log = LoggerFactory.getLog(JNDIConnectionProvider.class);

	private boolean alwaysLookup = false;

	private Object datasource;

	private Properties props;

	private String url;

	/**
	 * Constructor
	 * 
	 * @param jndiUrl
	 *          The url for the datasource
	 */
	public JNDIConnectionProvider(String jndiUrl, boolean alwaysLookup) {
		this.url = jndiUrl;
		this.alwaysLookup = alwaysLookup;
		init();
	}

	/**
	 * Constructor
	 * 
	 * @param jndiUrl
	 *          The URL for the DataSource
	 * @param jndiProps
	 *          The JNDI properties to use when establishing the InitialContext
	 *          for the lookup of the given URL.
	 */
	public JNDIConnectionProvider(String jndiUrl, Properties jndiProps, boolean alwaysLookup) {
		this.url = jndiUrl;
		this.props = jndiProps;
		this.alwaysLookup = alwaysLookup;
		init();
	}

	private void init() {

		if (!isAlwaysLookup()) {
			Context ctx = null;
			try {
				ctx = (props != null) ? new InitialContext(props) : new InitialContext();

				datasource = ctx.lookup(url);
			} catch (Exception e) {
				log.error("Error looking up datasource: " + e.getMessage(), e);
			} finally {
				if (ctx != null) {
					try {
						ctx.close();
					} catch (Exception ignore) {
					}
				}
			}
		}
	}

	@Override
	public Connection getConnection() throws SQLException {
		Context ctx = null;
		try {
			Object ds = this.datasource;

			if (ds == null || isAlwaysLookup()) {
				ctx = (props != null) ? new InitialContext(props) : new InitialContext();

				ds = ctx.lookup(url);
				if (!isAlwaysLookup()) {
					this.datasource = ds;
				}
			}

			if (ds == null) {
				throw new SQLException("There is no object at the JNDI URL '" + url + "'");
			}

			if (ds instanceof XADataSource) {
				return (((XADataSource) ds).getXAConnection().getConnection());
			} else if (ds instanceof DataSource) {
				return ((DataSource) ds).getConnection();
			} else {
				throw new SQLException("Object at JNDI URL '" + url + "' is not a DataSource.");
			}
		} catch (Exception e) {
			this.datasource = null;
			throw new SQLException("Could not retrieve datasource via JNDI url '" + url + "' " + e.getClass().getName() + ": " + e.getMessage());
		} finally {
			if (ctx != null) {
				try {
					ctx.close();
				} catch (Exception ignore) {
				}
			}
		}
	}

	public boolean isAlwaysLookup() {
		return alwaysLookup;
	}

	public void setAlwaysLookup(boolean b) {
		alwaysLookup = b;
	}

	/* 
	 * @see org.quartz.utils.ConnectionProvider#shutdown()
	 */
	@Override
	public void shutdown() throws SQLException {
		// do nothing
	}

}
